/*
 * Copyright (c) 2018-2999 湖南洛云网络科技有限公司 All rights reserved.
 *
 * https://www.gz-yami.com/
 *
 * 未经允许，不可做商业用途！
 *
 * 版权所有，侵权必究！
 */

package com.yami.shop.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yami.shop.bean.app.param.UserRegisterParam;
import com.yami.shop.bean.event.UpdateUserScoreEvent;
import com.yami.shop.bean.event.UserDataEvent;
import com.yami.shop.bean.model.User;
import com.yami.shop.bean.param.*;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.util.PageParam;
import com.yami.shop.common.util.RedisUtil;
import com.yami.shop.common.util.*;
import com.yami.shop.dao.UserMapper;
import com.yami.shop.service.UserExtensionService;
import com.yami.shop.service.UserService;
import lombok.AllArgsConstructor;
import ma.glasnost.orika.MapperFacade;
import org.apache.commons.collections.CollectionUtils;
import org.apache.poi.ss.usermodel.*;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * @author lgh on 2018/09/11.
 */
@Service
@AllArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final UserMapper userMapper;
    private final UserExtensionService userExtensionService;
    private final ApplicationEventPublisher eventPublisher;
    private final MapperFacade mapperFacade;
    private final PasswordEncoder passwordEncoder;

    @Override
    public User getUserByUserId(String userId) {
        return userMapper.selectById(userId);
    }

    @Override
    public void setUserLevelBylevelId(Integer level, Integer levelType) {
        userMapper.setUserLevelBylevelId(level, levelType);
        userExtensionService.setUserLevelBylevelId(level, levelType);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void setUserLevelByGrowth(Integer level, Integer minNeedGrowth, Integer maxNeedGrowth, Integer levelType) {
        userMapper.setMaxUserLevelBylevelId(level,minNeedGrowth ,maxNeedGrowth,levelType);
        userExtensionService.setMaxUserLevelByLevelId(level,minNeedGrowth ,maxNeedGrowth,levelType);
    }


    /**
     * 看看有没有校验验证码成功的标识
     * @param userRegisterParam
     * @param checkRegisterSmsFlag
     */
    @Override
    public void validate(UserRegisterParam userRegisterParam, String checkRegisterSmsFlag) {
        if (StrUtil.isBlank(userRegisterParam.getCheckRegisterSmsFlag())) {
            // 验证码已过期，请重新发送验证码校验
            throw new YamiShopBindException("yami.verification.expire");
        } else {
            String checkRegisterSmsFlagMobile = RedisUtil.get(checkRegisterSmsFlag);
            if (!Objects.equals(checkRegisterSmsFlagMobile, userRegisterParam.getMobile())) {
                // 验证码已过期，请重新发送验证码校验
                throw new YamiShopBindException("yami.verification.expire");
            }
        }
    }

    @Override
    public User getUserDetail(String userId) {
        return userMapper.getUserDetail(userId);
    }

    @Override
    public IPage<User> getUserPage(PageParam<User> page, User user) {
        return  userMapper.getUserPage(page,user);
    }

    @Override
    public Map<String, User> getUserListByUserNumbers(Set<String> keySet) {
        if (keySet.size() == 0){
            return new HashMap<>(16);
        }
        List<User> userList = userMapper.getUserListByUserNumbers(keySet);
        Map<String, User> userMap = userList.stream().collect(Collectors.toMap(User::getUserId,user -> user));
        return userMap;
    }

    @Override
    public CustomerRespParam countUserByParam(CustomerReqParam param) {
        CustomerRespParam respParam = new CustomerRespParam();
        Date endTime = param.getEndTime();
        Date startTime = param.getStartTime();
        // 累积粉丝数
        param.setAppId(1);
        param.setDateTime(endTime);
        if (Objects.isNull(param.getShopId())) {
            param.setMember(1);
            // 累计应该没有时间
            param.setStartTime(null);
            param.setEndTime(null);
            param.setDateTime(null);
            respParam.setFashNum(userMapper.countUserByParam(param));
            param.setDateTime(startTime);
            respParam.setPreFashNum(userMapper.countUserByParam(param));
            respParam.setFashNumRate(divRate(respParam.getPreFashNum(),respParam.getFashNum(),4));
        }

        // 累积会员数
        param.setDateTime(null);
        param.setMember(null);
        respParam.setMember( userMapper.countMemberByParam(param));
        param.setDateTime(endTime);
        respParam.setPreMember( userMapper.countMemberByParam(param));
        respParam.setMemberRate(divRate(respParam.getMember()-respParam.getPreMember(),respParam.getMember(),4));

        return respParam;
    }

    @Override
    public IPage<UserManagerParam> getUserInfoPage(PageParam<User> page, UserManagerReqParam user) {
        // user基本信息 积分 当前余额  累计金额
        Page<UserManagerParam> pages = new Page<>();
        pages.setCurrent(page.getCurrent());
        pages.setSize(page.getSize());
        List<UserManagerParam> list = userMapper.getUserPageByParam(new PageAdapter(page),user);
        pages.setRecords(list);
        Integer total = userMapper.countGetUserPageByParam(new PageAdapter(page), user);
        pages.setTotal(total);
        return pages;
    }

    @Override
    public IPage<UserManagerParam> getUserInfoPage(Page<User> page, UserManagerReqParam user) {
        // user基本信息 积分 当前余额  累计金额
        Page<UserManagerParam> pages = new Page<>();
        pages.setSize(page.getSize());
        pages.setCurrent(page.getCurrent());
        List<UserManagerParam> list = userMapper.getUserPageByParam(new PageAdapter(page),user);
        pages.setRecords(list);
        return pages;
    }

    @Override
    public UserManagerParam getuserInfo(String userId) {
        UserManagerParam param = userMapper.getuserInfo(userId);
        param.setCurrentScore(param.getScore());
        eventPublisher.publishEvent( new UserDataEvent(param));
        return param;
    }

    @Override
    public void downloadUserModel(HttpServletResponse response) {
        // 下载用户导入模板
        downloadUserModelExcel(response);
    }

    @Override
    public UserExcelParam parseUserImportFile(MultipartFile excelFile, List<UserLevelParam> userLevels) throws IOException {
        UserExcelParam resParam = new UserExcelParam();
        resParam.setSuccess(false);
        resParam.setImmediately(false);
        // 普通会员
        List<UserLevelParam> ordinary = userLevels.stream().filter(item -> Objects.equals(0, item.getLevelType())).collect(Collectors.toList());
        // 付费会员
        List<UserLevelParam> pay = userLevels.stream().filter(item -> Objects.equals(1, item.getLevelType())).collect(Collectors.toList());
        //读取工作簿
        Workbook workBook = WorkbookFactory.create(excelFile.getInputStream());
        // * 2.1不是Excel文件，直接返回提示信息
        if (!(excelFile.getOriginalFilename().contains("xls") || excelFile.getOriginalFilename().contains("xlsx"))) {
            // 格式错误
            throw  new YamiShopBindException("yami.file.error");
        }
        //读取工作表
        Sheet sheet = workBook.getSheetAt(0);
        int rowNums = sheet.getPhysicalNumberOfRows();
        //校验是否填写内容
        if (rowNums <= 1) {
            // 文件无内容
            throw new YamiShopBindException("yami.document.no.content");
        }
        // 有误信息条数
        int errorCount = 0;
        // 总计导入数据条数
        int total = 0;

        List<UserRegisterExcelParam> userList = new ArrayList<>();
        List<Integer> errorRows = new ArrayList<>();

        //循环读取每一行数据并校验
        for (int i = 1; i < rowNums; i++) {
            total++;
            try {
                //读取行
                Row row = sheet.getRow(i);
                int cell = 0;

                // 序号
                String serialNo = Objects.isNull(row.getCell(cell))? "":row.getCell(cell).getStringCellValue();
                // 手机号
                String phone = Objects.isNull(row.getCell(++cell))? "":row.getCell(cell).getStringCellValue();
                // 手机号必须是11位
                if (phone.length() != 11) {
                    errorRows.add(i + 1);
                    continue;
                }
                // 密码
                String password = Objects.isNull(row.getCell(++cell)) ? "" : row.getCell(cell).getStringCellValue();
                if (StrUtil.isBlank(password)) {
                    errorRows.add(i + 1);
                    continue;
                }
                // 会员类型(0普通会员/1付费会员)，不填默认为普通会员
                String levelType = Objects.isNull(row.getCell(++cell)) ? "" : row.getCell(cell).getStringCellValue();
                // vip结束时间
                String vipEndTime = Objects.isNull(row.getCell(++cell)) ? "" : row.getCell(cell).getStringCellValue();
                // 用户昵称
                String nickName = Objects.isNull(row.getCell(++cell)) ? "" : row.getCell(cell).getStringCellValue();
                // 邮箱
                String userMail = Objects.isNull(row.getCell(++cell)) ? "" : row.getCell(cell).getStringCellValue();
                // 性别
                String sex = Objects.isNull(row.getCell(++cell)) ? "" : row.getCell(cell).getStringCellValue();
                // 生日
                String birthDate = Objects.isNull(row.getCell(++cell)) ? "" : row.getCell(cell).getStringCellValue();
                // 备注
                String userMemo = Objects.isNull(row.getCell(++cell)) ? "" : row.getCell(cell).getStringCellValue();
                // 积分
                String score = Objects.isNull(row.getCell(++cell)) ? "" : row.getCell(cell).getStringCellValue();
                // 余额
                String balance = Objects.isNull(row.getCell(++cell)) ? "" : row.getCell(cell).getStringCellValue();
                // 成长值
                String growth = Objects.isNull(row.getCell(++cell)) ? "" : row.getCell(cell).getStringCellValue();

                UserRegisterExcelParam param = new UserRegisterExcelParam();
                param.setSerialNo(serialNo);
                param.setPhone(phone);
                param.setNickName(StrUtil.isBlank(nickName)?phone:nickName);
                param.setUserMail(userMail);
                param.setPassword(password);
                param.setSex(sex);
                param.setBirthDate(birthDate);
                param.setUserMemo(userMemo);
                int scoreValue = StrUtil.isBlank(score) ? 0 : new BigDecimal(score).intValue();
                param.setScore(scoreValue < 0 ? 0 : scoreValue);
                double balanceValue = StrUtil.isBlank(balance) ? 0.00 : new BigDecimal(balance).setScale(2).doubleValue();
                param.setBalance(balanceValue < 0 ? 0.00 : balanceValue);
                int growthValue = StrUtil.isBlank(growth) ? 0 : new BigDecimal(growth).intValue();
                param.setGrowth(growthValue < 0 ? 0 : growthValue);
                param.setLevelType(StrUtil.isBlank(levelType) ? 0 : new BigDecimal(levelType).intValue());
                if (Objects.equals(1,param.getLevelType()) && StrUtil.isBlank(vipEndTime)) {
                    errorRows.add(i + 1);
                    continue;
                }
                if (Objects.equals(1,param.getLevelType())) {
                    param.setVipEndTime(DateUtil.parse(vipEndTime,"yyyy-MM-dd HH:mm:ss"));
                }
                param.setLevel(getUserLevelByGrowthAndType(Objects.equals(0,param.getLevelType()) ? ordinary: pay,param.getGrowth()));
                userList.add(param);
            } catch (Exception e) {
                errorCount++;
                errorRows.add(i + 1);
                // 错误信息提示
                resParam.setRow(i+1);
                resParam.setMsg("第" + (i + 1) + "行数据有错误," + e.getMessage());
                resParam.setImmediately(true);
                return resParam;
            }
        }
        workBook.close();
        if (CollectionUtils.isNotEmpty(userList)) {
            resParam.setTotal(total);
            resParam.setSuccessNum(userList.size());
            resParam.setErrorNum(errorRows.size());
            resParam.setErrorRows(errorRows);
            resParam.setParam(Json.toJsonString(userList));
            resParam.setImmediately(false);
            resParam.setSuccess(true);
            return resParam;
        }
        resParam.setMsg("数据错误!导入0条");
        resParam.setImmediately(true);
        return resParam;
    }

    private Integer getUserLevelByGrowthAndType(List<UserLevelParam> levelParams, Integer growth) {
        int level = 1;
        for (UserLevelParam levelParam : levelParams) {
            if (growth >= levelParam.getNeedGrowth()) {
                level = levelParam.getLevel();
            }
        }
        return level;
    }

    private void downloadUserModelExcel(HttpServletResponse response) {
        //通过工具类创建writer
        ExcelWriter writer = ExcelUtil.getBigWriter();
        // 商品导出or模板
        List<String> headerList;
        // 用户昵称，手机号唯一标识，密码导入，地址不要
        String[] header = {"序号","手机号(必填)","密码(必填)","会员类型(0普通会员/1付费会员,必填)","vip结束时间(格式:2021-08-08 14:32:01，会员类型是1则必填)",
                "用户昵称(最多32个字)","邮箱","性别(M男/F女)","生日(格式:1989-08-08)","备注",
                "积分(限整数)","余额(精确小数点后两位)","成长值(限整数)"};
        headerList = Arrays.asList(header);
        Sheet sheet = writer.getSheet();
        writer.writeRow(headerList);

        // 列表设置为文本格式
        CellStyle style = writer.createCellStyle();
        Workbook wb = writer.getWorkbook();
        DataFormat format = wb.createDataFormat();
        style.setDataFormat(format.getFormat("@"));

        for (int i = 0; i < headerList.size(); i++) {
            if (i == 0) {
                sheet.setColumnWidth(i, 10 * 256);
                sheet.setDefaultColumnStyle(i,style);
            } else if (i == 3) {
                sheet.setColumnWidth(i, 30 * 256);
                style.setWrapText(true);
                sheet.setDefaultColumnStyle(i,style);
            } else if (i == 4) {
                sheet.setColumnWidth(i, 40 * 256);
                style.setWrapText(true);
                sheet.setDefaultColumnStyle(i,style);
            } else if (i == 9) {
                sheet.setColumnWidth(i, 40 * 256);
                sheet.setDefaultColumnStyle(i,style);
            } else {
                sheet.setColumnWidth(i, 20 * 256);
                sheet.setDefaultColumnStyle(i,style);
            }
        }
        // 会员类型 0普通会员 1付费会员
        String[] type = {"0", "1"};
        PoiExcelUtil.createDropDownList(sheet, Arrays.asList(type), 1, 50000, 3, 3);
        // 性别选项
        String[] sexList = {"M", "F"};
        PoiExcelUtil.createDropDownList(sheet, Arrays.asList(sexList), 1, 50000, 7, 7);
        PoiExcelUtil.writeExcel(response, writer);
    }


    private Double divRate(Integer a, Integer b,Integer scale) {
        if (Objects.isNull(b) || b == 0 || Objects.isNull(a)) {
            return 0.0;
        }
        return Arith.div(a,b,scale);
    }
}
